#include "base/Processor.h"
#include "base/Simulator.h"
#include "base/StatisticsCollection.h"
#include "util/Logger.h"

NEW_DELETE_OPERATORS_CPP(StatisticsCollection,100);

uint StatisticsCollection::globalCollectionID = 0;

StatisticsCollection::StatisticsCollection (const char* _cn, bool _rb) {
    collectionName = (char*)CALLOC(strlen(_cn)+1, sizeof(char));
    strcpy(collectionName, _cn);
    ASSERT(index(collectionName, ':') != NULL);

    rollbackEnabled = _rb;
    
    
    //Debug info
    collectionID = globalCollectionID++;
}

void StatisticsCollection::setDefaultRollbackBucket (Bucket* b) {
    rollbackBucket = b;
}

StatisticsCollection::~StatisticsCollection () {
    while (!counters.empty()) {
        Counter* cntPtr = counters.back();
        counters.pop_back();
        delete cntPtr;
    }
    while (!speculativeCounters.empty()) {
        SpeculativeCounter* cntPtr = speculativeCounters.back();
        speculativeCounters.pop_back();
        delete cntPtr;
    }
    while (!buckets.empty()) {
        Bucket* bktPtr = buckets.back();
        buckets.pop_back();
        delete bktPtr;
    }
    while (!rollbackBuckets.empty()) {
        Bucket* bktPtr = rollbackBuckets.back();
        rollbackBuckets.pop_back();
        delete bktPtr;
    }
    while (!histograms.empty()) {
        Histogram* h = histograms.back();
        histograms.pop_back();
        delete h;
    }

    FREE(collectionName);
}

Vector<Bucket*>* StatisticsCollection::getBuckets () {
    return (&buckets);
}

Bucket* StatisticsCollection::getBucket (const char* name) {
    ASSERT(!global_sim->isRunning());
    Vector<Bucket*>::iterator bktIdx;
    for (bktIdx = buckets.begin();
         bktIdx != buckets.end();
         bktIdx++)
    {
        if (strcmp((*bktIdx)->outputName, name) == 0) {
            return (*bktIdx);
        }
    }
    Log->e("Bucket %s was not found in the stats collection.\n", name);
    return NULL;
}

Bucket* StatisticsCollection::addRollbackBucket (const char* outputName) {
    Bucket* bktptr = NEW Bucket(outputName);
    rollbackBuckets.push_back(bktptr);
    return bktptr;
}

Bucket* StatisticsCollection::addBucket (const char* outputName,
                                         Bucket* rollbackRecipient)
{
    Bucket* bktptr;
    if (rollbackEnabled) {
        if (rollbackRecipient) {
            bktptr = NEW Bucket(outputName, rollbackRecipient);
        } else {
            bktptr = NEW Bucket(outputName, rollbackBucket);
        }
    } else {
        bktptr = NEW Bucket(outputName);
    }
    buckets.push_back(bktptr);
    return bktptr;
}

Counter* StatisticsCollection::addCounter (const char* outputName) {
    Counter* cntptr = NEW Counter(outputName);
    counters.push_back(cntptr);
    return cntptr;
}

SpeculativeCounter* StatisticsCollection::addSpeculativeCounter (const char* outputName,
                                                                 const char* outputRollbackName) {
    SpeculativeCounter* cntptr = NEW SpeculativeCounter(outputName,
                                                        outputRollbackName);
    speculativeCounters.push_back(cntptr);
    return cntptr;
}

Histogram* StatisticsCollection::addHistogram (const char* outputName, int size) {
    Histogram* h = NEW Histogram(size, outputName);
    histograms.push_back(h);
    return h;
}

Counter* StatisticsCollection::getCounter (const char* name) {
    ASSERT(!global_sim->isRunning());
    Vector<Counter*>::iterator counti;
    for (counti = counters.begin();
         counti != counters.end();
         counti++)
    {
        if (strcmp((*counti)->outputName, name) == 0) 
            return (*counti);
    }
    Log->e("Counter %s was not found in the stats collection.\n", name);
    return NULL;
}

void StatisticsCollection::printStatisticsCollection (FILE* statFile, const char* prefix) {
    fprintf(statFile, "%s%s\n", prefix, collectionName);
    printBuckets(statFile, prefix);
    printCounters(statFile, prefix);
    printSpeculativeCounters(statFile, prefix);
    printHistograms(statFile);
}
    
void StatisticsCollection::printCounters (FILE* statFile, const char* prefix) {
    
    Vector<Counter*>::iterator counterIterator;
    for (counterIterator = counters.begin(); 
         counterIterator != counters.end(); 
         counterIterator++)
    {
        fprintf(statFile,
                "%s%s: %lld\n",
                prefix,
                (*counterIterator)->outputName, 
                (*counterIterator)->value);
    }
}

void StatisticsCollection::printSpeculativeCounters (FILE* statFile, const char* prefix) {
    
    Vector<SpeculativeCounter*>::iterator counterIterator;
    for (counterIterator = speculativeCounters.begin(); 
         counterIterator != speculativeCounters.end(); 
         counterIterator++)
    {
        fprintf(statFile,
                "%s%s: %lld\n",
                prefix,
                (*counterIterator)->outputName, 
                (*counterIterator)->value);
        if (strlen((*counterIterator)->outputRollbackName) > 0) {
            fprintf(statFile,
                    "%s%s: %lld\n",
                    prefix,
                    (*counterIterator)->outputRollbackName, 
                    (*counterIterator)->valueRollback);
        }
    }
}

void StatisticsCollection::printBuckets (FILE* statFile, const char* prefix){
    Vector<Bucket*>::iterator bucketIterator;
    uint64 total = 0;

    for (bucketIterator = buckets.begin(); 
         bucketIterator != buckets.end(); 
         bucketIterator++)
    {
        fprintf(statFile,
                "%s%s: %lld\n",
                prefix,
                (*bucketIterator)->outputName,
                (*bucketIterator)->value);
        total += (*bucketIterator)->value;
    }
    for (bucketIterator = rollbackBuckets.begin(); 
         bucketIterator != rollbackBuckets.end(); 
         bucketIterator++)
    {
        fprintf(statFile,
                "%s%s: %lld\n",
                prefix,
                (*bucketIterator)->outputName,
                (*bucketIterator)->value);
        total += (*bucketIterator)->value;
    }
    fprintf(statFile, "%sTotal-Bucket-Cycles: %lld\n", prefix, total);
}

void StatisticsCollection::printHistograms (FILE* statFile) {
    Vector<Histogram*>::iterator histIterator;
    for (histIterator = histograms.begin(); 
         histIterator != histograms.end(); 
         histIterator++)
    {
        (*histIterator)->printStats(statFile, "#");
    }
}

void StatisticsCollection::rollback () {
    Vector<Bucket*>::iterator bktIdx;
    for (bktIdx = buckets.begin();
         bktIdx != buckets.end();
         bktIdx++)
    {
        (*bktIdx)->rollback();
    }
}

void StatisticsCollection::rollbackSpeculativeCounters () {
    for (unsigned i = 0; i < speculativeCounters.size(); i++) {
        speculativeCounters[i]->rollback();
    }
}

void StatisticsCollection::commit () {
    Vector<Bucket*>::iterator bktIdx;
    for (bktIdx = buckets.begin();
         bktIdx != buckets.end();
         bktIdx++)
    {
        (*bktIdx)->commit();
    }
}

void StatisticsCollection::commitSpeculativeCounters () {
    for (unsigned i = 0; i < speculativeCounters.size(); i++) {
        speculativeCounters[i]->commit();
    }
}

uint64 StatisticsCollection::getSpeculativeCountersSpeculativeSum () {
    uint64 sum = 0;
    for (unsigned i = 0; i < speculativeCounters.size(); i++) {
        sum += speculativeCounters[i]->valueSpeculative;
    }
    return sum;
}
